import 'package:flutter/material.dart';

import 'calendar_weekday_model.dart';
import 'calendar_scroll_physics.dart';
import 'calendar_util.dart';

typedef DayWidgetBuilder = Widget Function(
    DateTime date, bool isLastMonthDay, bool isNextMonthDay);

typedef CurrentPageDate = void Function(DateTime dateTime); //获取当前page的日期
typedef CurrentPageRowCount = void Function(int iRowCount); //获取当前page行数

//-------------------轮播-------------------
class CalendarCarousel extends StatefulWidget {
  final DateTime startTime; // 启始日期
  final DateTime endTime; // 结束日期
  final DateTime currentPageTime; // 当前要显示的日期, 默认当前时间

  final CurrentPageDate currentPageDate;
  final CurrentPageRowCount currentPageRowCount;
  final CalendarWeekdayModel weekdayHeader;
  final DayWidgetBuilder dayWidgetBuilder;
  final double childAspectRatio;

  final CalendarShow calendarShow;
  final CalendarController calendarController;
  final double shrinkageHeight; // 需要收缩的高度

  final Color tBgColor;
  //final double fScreenWidth;
  final double fLeftPandingVal;
  final double fScreenWidth;

  CalendarCarousel({
    Key? key,
    required this.startTime,
    required this.endTime,
    required this.weekdayHeader,
    required this.currentPageDate,
    required this.currentPageRowCount,
    required this.dayWidgetBuilder,
    this.childAspectRatio = 1.2,
    this.calendarShow = CalendarShow.CalendarShowWeek,
    this.tBgColor = const Color.fromRGBO(245, 246, 249, 1),
    //this.fScreenWidth = 393, //lufei 日历滑动bug
    this.fLeftPandingVal = 0,
    required this.calendarController,
    this.shrinkageHeight = 1,
    currentPageTime,
    pageController,
    required this.fScreenWidth,
  })  : currentPageTime = currentPageTime ?? DateTime.now(),
        assert(!(currentPageTime.isBefore(startTime))),
        assert(!(endTime.isBefore(startTime))),
        assert(!(endTime.isBefore(currentPageTime))),
        super(key: key);

  @override
  _PTCalendarCarouselState createState() => _PTCalendarCarouselState();
}

class _PTCalendarCarouselState extends State<CalendarCarousel>
    with TickerProviderStateMixin {
  late PageController _pageController;
  late CalendarScrollPhysics _scrollPhysics;
  late ScrollController _monthController;

  late CalendarUtil _calendarUtil;
  late CalendarController _calendarController;
  double _aspectRatio = 1;
  double _monthRatio = 1;
  double _weekRatio = 1;

  Color tColorBg = const Color.fromRGBO(245, 246, 249, 1);

  // double fScreenWidth =
  //     PTCommonUtil.winSize.width; //ScreenUtil.screenWidth; //lufei
  double fScreenWidth = 393;
  double fLeftPandVal = 0;

  @override
  void initState() {
    fScreenWidth = widget.fScreenWidth;
    fLeftPandVal = widget.fLeftPandingVal;

    _calendarUtil = CalendarUtil(
      initTime: widget.currentPageTime,
      firstDayOfWeek: widget.weekdayHeader.firstDayOfWeek,
      childAspectRatio: widget.childAspectRatio,
      calendarShow: widget.calendarShow,
    );
    _pageController = PageController(
      viewportFraction: 1,
      initialPage: _calendarUtil.initIndex(),
      keepPage: false,
    );

    _monthController = ScrollController(
      keepScrollOffset: true,
    );

    _calendarController = widget.calendarController;
    _calendarController._pageController = _pageController;
    _calendarController._calendarUtil = _calendarUtil;
    _calendarController._changeCalendarShow = _changeCalendarShow;
    _calendarController._updateOffset = _updateOffset;
    _calendarController._fScreenWidth = fScreenWidth;

    _scrollPhysics = CalendarScrollPhysics(Status(), _calendarUtil);

    _calendarController._scrollPhysics = _scrollPhysics;

    _monthRatio = _calendarUtil.getAspectRatio(widget.currentPageTime,
        show: CalendarShow.CalendarShowMonth, rowCountList: []);
    _weekRatio = _calendarUtil.getAspectRatio(widget.currentPageTime,
        show: CalendarShow.CalendarShowWeek, rowCountList: []);
    _aspectRatio = _calendarUtil.calendarShow == CalendarShow.CalendarShowMonth
        ? _monthRatio
        : _weekRatio;

    super.initState();

    // 修改最大最小范围
    _setScrollPhysicMaxMin();
  }

  @override
  void dispose() {
    _pageController.dispose();
    _monthController.dispose();
    _calendarController.dispose();
    super.dispose();
  }

  _changeCalendarShow(CalendarShow calendarShow, DateTime dateTime) {
    Future.delayed(Duration.zero, () {
      var date = dateTime;

      _calendarUtil.calendarShow = calendarShow;

      var currentPage = _calendarUtil.indexOf(date);
      _pageController.jumpToPage(currentPage);
      _aspectRatio =
          _calendarUtil.calendarShow == CalendarShow.CalendarShowMonth
              ? _monthRatio
              : _calendarUtil.getAspectRatio(widget.currentPageTime,
                  show: CalendarShow.CalendarShowWeek, rowCountList: []);
      _setScrollPhysicMaxMin();
      _updateOffset();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Offstage(
          // ignore: unnecessary_null_comparison
          offstage: widget.weekdayHeader.builder == null,
          child: _weekDayWidget(),
        ),
        _calendarDayWidget(),
      ],
    );
  }

  Widget _calendarDayWidget() {
    double shrinRatio =
        fScreenWidth / (fScreenWidth / _monthRatio - widget.shrinkageHeight);

    shrinRatio = double.parse((shrinRatio).toStringAsFixed(14));

    // 更新ziview的位移
    _updateOffset();

    if (_calendarUtil.calendarShow == CalendarShow.CalendarShowMonth) {
      _aspectRatio = shrinRatio;
    } else {
      _aspectRatio = _weekRatio;
    }

    return AspectRatio(
      aspectRatio:
          _aspectRatio, //  MediaQuery.of(context).size.width / _aspectRatio
      child: PageView.builder(
        onPageChanged: _pageChanged,
        controller: _pageController,
        physics: _scrollPhysics,
        itemBuilder: (BuildContext context, int index) {
//         return IndexedStack(
//            index: _calendarUtil.calendarShow == CalendarShow.CalendarShowMonth ? 0 : 1,
//            children: <Widget>[
//              _monthView(index),
//              _weekView(index),
//            ],
//          );
          if (_calendarUtil.calendarShow == CalendarShow.CalendarShowMonth) {
            return _monthView(index);
          } else {
            return _weekView(index);
          }
        },
        scrollDirection: Axis.horizontal,
      ),
    );
  }

  Widget _monthView(int index) {
    DateTime time = _calendarUtil.getActualDate(index, widget.currentPageTime);
    if (_calendarUtil.calendarShow == CalendarShow.CalendarShowWeek) {
      time = DateTime(
          widget.currentPageTime.year, widget.currentPageTime.month, 1);
    }

    int year = time.year;
    int month = time.month;
    var firstWeekDay = DateTime(year, month, 1).weekday % 7;
    var lastMonthRestDayCount =
        (firstWeekDay - (widget.weekdayHeader.firstDayOfWeek % 7)) % 7;

    var thisMonthDayCount = DateTime(year, month + 1, 0).day;
    var lastMonthDayCount = DateTime(year, month, 0).day;

    /// 行数
    var rowCount = ((thisMonthDayCount + lastMonthRestDayCount) / 7.0).ceil();

    return Container(
      width: double.infinity,
      margin: EdgeInsets.only(left: fLeftPandVal, right: fLeftPandVal),
      color: tColorBg,
      child: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 7,
          childAspectRatio: widget.childAspectRatio,
        ),
        itemCount: rowCount * 7,
        itemBuilder: (BuildContext context, int index) {
          var currentDay = index + 1 - lastMonthRestDayCount;
          // last month day
          var isLastMonthDay = false;
          // next month day
          var isNextMonthDay = false;

          var currentMonth = month;
          if (currentDay <= 0) {
            isLastMonthDay = true;
            currentDay = lastMonthDayCount + currentDay;
            currentMonth -= 1;
          } else if (currentDay > thisMonthDayCount) {
            isNextMonthDay = true;
            currentDay = currentDay - thisMonthDayCount;

            currentMonth += 1;
          }
          //
          return widget.dayWidgetBuilder(
              DateTime(year, currentMonth, currentDay),
              isLastMonthDay,
              isNextMonthDay);
        },
        controller: _monthController,
        physics: NeverScrollableScrollPhysics(),
      ),
    );
  }

  Widget _weekView(int index) {
    DateTime currentDate =
        _calendarUtil.getActualDate(index, widget.currentPageTime);

    return Container(
      width: double.infinity,
      margin: EdgeInsets.only(left: fLeftPandVal, right: fLeftPandVal),
      color: tColorBg,
      child: GridView.count(
//        physics: NeverScrollableScrollPhysics(),
        crossAxisCount: 7,
        childAspectRatio: widget.childAspectRatio,
        children: List.generate(7, (index) {
          // 左边的日期个数
          var leftCount = (currentDate.weekday % 7) -
              (widget.weekdayHeader.firstDayOfWeek % 7);

          var date = currentDate.add(Duration(days: index - leftCount));

          // last month day
          var isLastMonthDay = date.month < currentDate.month;
          // next month day
          var isNextMonthDay = date.month > currentDate.month;
          return widget.dayWidgetBuilder(date, isLastMonthDay, isNextMonthDay);
        }),
      ),
    );
  }

  _pageChanged(int index) {
    DateTime currentTime = widget.currentPageTime;
    currentTime = _calendarController._currentTime;
    _calendarController._currentTime;
    DateTime currentPageTime = _calendarUtil.getActualDate(index, currentTime);

    if (currentPageTime.isBefore(widget.startTime)) {
      currentPageTime = widget.startTime;
    }
    List<int> rowCountList = []; //获取行数
    _monthRatio = _calendarUtil.getAspectRatio(currentPageTime,
        show: CalendarShow.CalendarShowMonth, rowCountList: rowCountList);
    _weekRatio = _calendarUtil.getAspectRatio(currentPageTime,
        show: CalendarShow.CalendarShowWeek, rowCountList: []);
    _aspectRatio = _calendarUtil.calendarShow == CalendarShow.CalendarShowMonth
        ? _monthRatio
        : _weekRatio;
    widget.currentPageDate(currentPageTime);

    if (rowCountList.isNotEmpty) {
      widget.currentPageRowCount(rowCountList[0]);
    }
  }

  _setScrollPhysicMaxMin() {
    // int startIndex = _calendarUtil.indexOf(widget.startTime);
    // int endIndex = _calendarUtil.indexOf(widget.endTime);
    // print("================ startIndex=$startIndex  endIndex=$endIndex");
    _scrollPhysics.status.minScrollExtent =
        fScreenWidth * _calendarUtil.indexOf(widget.startTime);
    _scrollPhysics.status.maxScrollExtent =
        fScreenWidth * _calendarUtil.indexOf(widget.endTime);
  }

  List<Widget> _renderWeekDays() {
    var list = [];
    for (int i = 0; i < 7; i++) {
      var weekday = (widget.weekdayHeader.firstDayOfWeek + i) % 7;
      if (weekday == 0) weekday = 7;
      list.add(weekday);
    }
    return list.map((i) => widget.weekdayHeader.builder(i)).toList();
  }

  Widget _weekDayWidget() {
    return Container(
      width: double.infinity,
      margin: EdgeInsets.only(left: fLeftPandVal, right: fLeftPandVal),
      height: widget.weekdayHeader.height,
      color: tColorBg,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: _renderWeekDays(),
      ),
    );
  }

  void _updateOffset() {
    if (widget.shrinkageHeight == 0 ||
        _calendarUtil.calendarShow == CalendarShow.CalendarShowWeek) return;

    // 选中的日期在第几行，
    var currentRow = CalendarUtil.getCurrentRow(
        widget.weekdayHeader.firstDayOfWeek, widget.currentPageTime);
    // 一共有几行
    var rowCount = CalendarUtil.getRowCount(widget.currentPageTime.year,
        widget.currentPageTime.month, widget.weekdayHeader.firstDayOfWeek);
    // 上面几行,上面占用百分比
    double persent = (currentRow - 1.0) / (rowCount - 1.0);

    double offset = persent * widget.shrinkageHeight;

    if (_monthController.hasClients) {
      //print("========== jumpTo $offset");
      _monthController.jumpTo(offset);
    } else {
      //print("失败失败=============失败失败   $offset");
//      Future.delayed(Duration(milliseconds: 1), () {
//        _updateOffset();
//      });
    }
  }
}

typedef ChangeCalendarShow = void Function(
    CalendarShow calendarShow, DateTime dateTime);

typedef GetMaxHeight = double Function();

typedef GetMinHeight = double Function();

typedef UpdateOffset = void Function();

//----------------------控制器
class CalendarController extends ChangeNotifier {
  late ChangeCalendarShow _changeCalendarShow;

  late PageController _pageController;
  late CalendarUtil _calendarUtil;
  late UpdateOffset _updateOffset;
  late CalendarScrollPhysics _scrollPhysics;
  late DateTime _currentTime = DateTime.now();
  late double _fScreenWidth;

  void nextPage(
      {Duration? duration,
      Curve curve = Curves.bounceInOut,
      bool bIsNextYear = false}) {
    int index = (_pageController.page! + 1).toInt();
    if (bIsNextYear) {
      index = (_pageController.page! + 12).toInt();
    }
    double width = _fScreenWidth * index;
    if (_scrollPhysics.status.minScrollExtent > width) {
      return;
    } else if (_scrollPhysics.status.maxScrollExtent < width) {
      return;
    }

    if (duration != null) {
      _pageController.nextPage(duration: duration, curve: curve);
    } else {
      _pageController.jumpToPage(index);
    }
  }

  void previousPage(
      {Duration? duration,
      Curve curve = Curves.bounceInOut,
      bool bIsNextYear = false}) {
    int index = (_pageController.page! - 1).toInt();
    if (bIsNextYear) {
      index = (_pageController.page! - 12).toInt();
    }
    double width = _fScreenWidth * index;
    if (_scrollPhysics.status.minScrollExtent > width) {
      return;
    } else if (_scrollPhysics.status.maxScrollExtent < width) {
      return;
    }

    if (duration != null) {
      _pageController.previousPage(duration: duration, curve: curve);
    } else {
      _pageController.jumpToPage(index);
    }
  }

  void goToToday({Duration? duration, Curve curve = Curves.bounceInOut}) {
    var now = DateTime.now();
    goToDate(dateTime: now, duration: duration, curve: curve);
  }

  void goToDate(
      {required DateTime dateTime,
      Duration? duration,
      Curve curve = Curves.bounceInOut}) {
    _currentTime = dateTime;
    int index = _calendarUtil.indexOf(dateTime);

    double width = _fScreenWidth * index;
    if (_scrollPhysics.status.minScrollExtent > width) {
      return;
    } else if (_scrollPhysics.status.maxScrollExtent < width) {
      return;
    }

    if (duration != null) {
      _pageController.animateToPage(index, duration: duration, curve: curve);
    } else {
      _pageController.jumpToPage(index);
    }
  }

  void changeCalendarShow(CalendarShow calendarShow, DateTime dateTime) {
    _changeCalendarShow(calendarShow, dateTime);
  }

  double getMaxHeight(double childAspectRatio, int firstDayOfWeek,
      DateTime currentSelectedTime) {
    double radio = CalendarUtil.aspectRatio(childAspectRatio, firstDayOfWeek,
        currentSelectedTime, CalendarShow.CalendarShowMonth);
    return _fScreenWidth / radio;
  }

  double getMinHeight(double childAspectRatio, int firstDayOfWeek,
      DateTime currentSelectedTime) {
    double radio = CalendarUtil.aspectRatio(childAspectRatio, firstDayOfWeek,
        currentSelectedTime, CalendarShow.CalendarShowWeek);
    return _fScreenWidth / radio;
  }

  void updateOffset() {
    _updateOffset();
  }
}
